import java.awt.Color;


public class Ellipsoid implements Renderable{
	
	private final double cx;
	private final double cy;
	private final double cz;
	private final double[][] transformationMatrix;
	private final double[][] InverseTransformation;
	private final int materialIndex; 
	
	public Ellipsoid(double cx,double cy,double cz,
					 double a0,double a1,double a2,
					 double b0,double b1,double b2,
					 double c0,double c1,double c2, 
					 int index){
		this.cx=cx;
		this.cy=cy;
		this.cz=cz;
		transformationMatrix=new double[3][3];
		transformationMatrix[0][0]=a0;
		transformationMatrix[0][1]=a1;
		transformationMatrix[0][2]=a2;
		transformationMatrix[1][0]=b0;
		transformationMatrix[1][1]=b1;
		transformationMatrix[1][2]=b2;
		transformationMatrix[2][0]=c0;
		transformationMatrix[2][1]=c1;
		transformationMatrix[2][2]=c2;
		this.materialIndex = index;
		this.InverseTransformation = this.InverseTransformationMatrix();
		
	}
	
	public double getcx(){
		return cx;
	}

	public double getcy(){
		return cy;
	}
	
	public double getcz(){
		return cz;
	}
	
	public double[][] getTransformationMatrix(){
		return transformationMatrix;
	}
	
	@Override
	public int getMaterial(){
		return this.materialIndex;
	}
	
	public Vector getCenter(){
		return new Vector(cx, cy, cz);
	}
	
	
	private double[][] InverseTransformationMatrix(){
		double det = (transformationMatrix[0][0]*(transformationMatrix[1][1]*transformationMatrix[2][2] 
				- transformationMatrix[1][2]*transformationMatrix[2][1]) - transformationMatrix[0][1]*(transformationMatrix[1][0]*transformationMatrix[2][2]
				- transformationMatrix[1][2]*transformationMatrix[2][0]) + transformationMatrix[0][2]*(transformationMatrix[1][0]*transformationMatrix[2][1]
				- transformationMatrix[1][1]*transformationMatrix[2][0]));
		
		
		double[][] Mt = {{transformationMatrix[0][0], transformationMatrix[1][0], transformationMatrix[2][0]}, 
						{transformationMatrix[0][1], transformationMatrix[1][1], transformationMatrix[2][1]}, 
						{transformationMatrix[0][2], transformationMatrix[1][2], transformationMatrix[2][2]}};
		
		double[][] M1 = {{Mt[1][1]*Mt[2][2] - Mt[1][2]*Mt[2][1], Mt[1][2]*Mt[2][0] - Mt[1][0]*Mt[2][2], Mt[1][0]*Mt[2][1] - Mt[1][1]*Mt[2][0]},
				{Mt[0][2]*Mt[2][1] - Mt[0][1]*Mt[2][2], Mt[0][0]*Mt[2][2] - Mt[0][2]*Mt[2][0], Mt[0][1]*Mt[2][0] - Mt[0][0]*Mt[2][1]}, 
				{Mt[0][1]*Mt[1][2] - Mt[0][2]*Mt[1][1], Mt[0][2]*Mt[1][0] - Mt[0][0]*Mt[1][2], Mt[0][0]*Mt[1][1] - Mt[0][1]*Mt[1][0]}};
		
		return new double[][] {{M1[0][0]*(1/det), M1[0][1]*(1/det), M1[0][2]*(1/det)},
							  {M1[1][0]*(1/det), M1[1][1]*(1/det), M1[1][2]*(1/det)},
							  {M1[2][0]*(1/det), M1[2][1]*(1/det), M1[2][2]*(1/det)}};  
		
				
	}
	
	
	@Override
	public double intersect(Ray r) {
		
		Vector cam = r.getOrigin().multiplyMatrixByVector(transformationMatrix);
		Vector dir = r.getDirection().multiplyMatrixByVector(transformationMatrix).normalizedVector();
		
		Vector center = new Vector(cx, cy, cz);
		Vector L = center.subtructVector(cam);

		double t_ca = L.dotProduct(dir);

		if (t_ca < 0) return -1;
		
		double d2 = L.dotProduct(L) - t_ca * t_ca;

		if (d2 > 1) return -1;
		// t1 = tca - thc and t2 = tca + thc, thc positive double value so we take the t1 automatically
		// Transfer the distance t back to original coordinates
		
		Vector intersectPoint = cam.addVectors(dir.multiplyVectorByScalar(t_ca - Math.sqrt(1 - d2)));
		return r.getOrigin().distance(intersectPoint.multiplyMatrixByVector(InverseTransformation));
		
	
	}
	
	@Override
	public double getT(Ray ray) {
		Vector inverseCenter = getCenter().multiplyMatrixByVector(getTransformationMatrix());
		Sphere transSphere = new Sphere(inverseCenter.getvx(), inverseCenter.getvy(), inverseCenter.getvz(), 1, materialIndex);
		Ray transRay = new Ray(ray.getOrigin().multiplyMatrixByVector(getTransformationMatrix()), ray.getDirection().multiplyMatrixByVector(getTransformationMatrix()).normalizedVector());
		double t = transSphere.getT(transRay);
		if (t < 0) return t;

		transRay.setT(t);
		Vector crossingPoint = transRay.getOrigin().addVectors(transRay.getDirection().multiplyVectorByScalar(transRay.getT()));
		crossingPoint = crossingPoint.multiplyMatrixByVector(InverseTransformation);
		Vector basePoint = transRay.getOrigin().multiplyMatrixByVector(InverseTransformation);

		t = basePoint.distance(crossingPoint);
					
		return t;
	}
	
	
}
